/**
 * @file meter.cpp
 * @author LinTeX9527 (lintex9527@yeah.net)
 * @brief 电子测光表主逻辑代码
 * @version 0.1
 * @date 2024-12-03
 *
 * @copyright Copyright (c) 2024
 *
 */

#include <Arduino.h>
#include <math.h>
#include "gui_guider.h"
#include "meter.h"

#ifndef ARRAY_SIZE
#define ARRAY_SIZE(arr)             (sizeof((arr)) / sizeof((arr)[0]))
#endif

/* Global Variables ------------------------------------------------------------------------------*/

meter_mode_e g_meter_mode = METER_MODE_AV; /* 测光模式，只能是 AV/TV 模式 */

meter_mode_e meter_mode_get(void)
{
	return g_meter_mode;
}

meter_mode_e meter_mode_switch(void)
{
	switch (g_meter_mode)
	{
	case METER_MODE_AV:
		// 切换到 TV 模式
		g_meter_mode = METER_MODE_TV;
		break;
	case METER_MODE_TV:
		// 切换到 AV 模式
		g_meter_mode = METER_MODE_AV;
		break;

	default:
		break;
	}

	return g_meter_mode;
}


typedef struct {
	int   idx; /* 索引编号     */
	float val; /* 数值表示形式  */
	const char* str; /* 字符串表达形式 */
} val_str_t;


/*---------------------------------------  ISO 感光度  --------------------------------------------*/

const val_str_t g_iso_list[] = { // 感光度列表
	/*  idx    val    str   */
	{     0,    100,   "100"   },
	{     1,    200,   "200"   },
	{     2,    400,   "400"   },
	{     3,    800,   "800"   },
	{     4,    1600,  "1600"  },
	{     5,    3200,  "3200"  },
	{     6,    6400,  "6400"  },
	{     7,   12800,  "12800" },
};
const uint32_t g_iso_list_size = ARRAY_SIZE(g_iso_list);
uint32_t g_iso_idx = 0;

/**
 * @brief ISO 档位
 *
 * @param
 * @return
 */
uint32_t iso_get_idx(void)
{
	return g_iso_idx;
}

/**
 * @brief ISO当前档位的数值形式
 *
 * @param
 * @return
 */
float iso_get_val(void)
{
	return g_iso_list[g_iso_idx].val;
}

/**
 * @brief ISO当前档位的字符串形式
 *
 * @param
 * @return
 */
const char* iso_get_string(void)
{
	return g_iso_list[g_iso_idx].str;
}

/**
 * @brief ISO档位自增1，达到最大值则恢复到0。取值范围 [0, g_iso_list_size - 1]
 *
 * @param
 * @return
 */
uint32_t iso_idx_increment(void)
{
	g_iso_idx = (g_iso_idx + 1 + g_iso_list_size) % g_iso_list_size;
	return g_iso_idx;
}

/**
 * @brief ISO档位减1,0往下又回到最大值。取值范围 [0, g_iso_list_size - 1]
 *
 * @return
 */
uint32_t iso_idx_decrement(void)
{
	g_iso_idx = (g_iso_idx - 1 + g_iso_list_size) % g_iso_list_size;
	return g_iso_idx;
}

/*---------------------------------------  AV 光圈大小  --------------------------------------------*/

const val_str_t g_av_list[] = { // 光圈大小列表
	{     0,    1,     "1/1.0" },
	{     1,    1.4,   "1/1.4" },
	{     2,    2,     "1/2.0" },
	{     3,    2.8,   "1/2.8" },
	{     4,    4,     "1/4" },
	{     5,    5.6,   "1/5.6" },
	{     6,    8,     "1/8" },
	{     7,    11,    "1/11" },
	{     8,    16,    "1/16" },
	{     9,    22,    "1/22" },
	{     10,   32,    "1/32" },
	{     11,   45,    "1/45" },
	{     12,   64,    "1/64" },
	{     13,   90,    "1/90" },
};
const uint32_t g_av_list_size = ARRAY_SIZE(g_av_list);
uint32_t g_av_idx = 4;

/**
 * @brief AV 档位
 *
 * @param
 * @return
 */
uint32_t av_get_idx(void)
{
	return g_av_idx;
}

void av_set_idx(uint32_t id)
{
	if ((0 <= id) && (id < g_av_list_size)) {
		g_av_idx = id;
	}
}

/**
 * @brief AV 数值
 *
 * @param
 * @return
 */
float av_get_val(void)
{
	return g_av_list[g_av_idx].val;
}

/**
 * @brief AV 字符串形式
 *
 * @param
 * @return
 */
const char* av_get_string(void)
{
	return g_av_list[g_av_idx].str;
}

/**
 * @brief AV 档位自加1，加大最大值又回到0.取值范围 [0, g_av_list_size - 1]
 *
 * @param
 * @return
 */
uint32_t av_idx_increment(void)
{
	g_av_idx = (g_av_idx + 1 + g_av_list_size) % g_av_list_size;
	return g_av_idx;
}

/**
 * @brief AV 索引编号自减1，0往下又回到最大值。取值范围 [0, g_av_list_size - 1]
 *
 * @return
 */
uint32_t av_idx_decrement(void)
{
	g_av_idx = (g_av_idx - 1 + g_av_list_size) % g_av_list_size;
	return g_av_idx;
}

/*---------------------------------------  TV 快门速度  --------------------------------------------*/

const val_str_t g_tv_list[] = { // 快门速度列表
	{     0,    32,       "32" },
	{     1,    16,       "16" },
	{     2,    8,        "8" },
	{     3,    4,        "4" },
	{     4,    2,        "2" },
	{     5,    1,        "1" },
	{     6,    1.0/2,    "1/2" },
	{     7,    1.0/4,    "1/4" },
	{     8,    1.0/8,    "1/8" },
	{     9,    1.0/15,   "1/15" },
	{    10,    1.0/30,   "1/30" },
	{    11,    1.0/60,   "1/60" },
	{    12,    1.0/125,  "1/125" },
	{    13,    1.0/250,  "1/250" },
	{    14,    1.0/500,  "1/500" },
	{    15,    1.0/1000, "1/1000" }
};
const uint32_t g_tv_list_size = ARRAY_SIZE(g_tv_list);
uint32_t g_tv_idx = 0;

/**
 * @brief TV 的档位
 *
 * @param
 * @return
 */
uint32_t tv_get_idx(void)
{
	return g_tv_idx;
}

/**
 * @brief 传入外来的数值更新 TV 档位
 *
 * @param idx
 */
void tv_set_idx(uint32_t idx)
{
	if ((idx >= 0) && (idx < g_tv_list_size)) {
		g_tv_idx = idx;
	}
}

/**
 * @brief 当前档位的 TV 数值
 *
 * @param
 * @return
 */
float tv_get_val(void)
{
	return g_tv_list[g_tv_idx].val;
}

/**
 * @brief 当前档位的 TV 字符串形式
 *
 * @param
 * @return
 */
const char* tv_get_string(void)
{
	return g_tv_list[g_tv_idx].str;
}

/**
 * @brief TV档位自加1，达到最大值回归0。取值范围 [0, g_tv_list_size - 1]
 *
 * @param
 * @return 自加后的 TV 档位值
 */
uint32_t tv_idx_increment(void)
{
	g_tv_idx = (g_tv_idx + 1 + g_tv_list_size) % g_tv_list_size;
	return g_tv_idx;
}

/**
 * @brief TV档位自减1, 0 再减1回到最大值。取值范围 [0, g_tv_list_size -1]
 *
 * @param
 * @return
 */
uint32_t tv_idx_decrement(void)
{
	g_tv_idx = (g_tv_idx - 1 + g_tv_list_size) % g_tv_list_size;
	return g_tv_idx;
}

/*------------------------------------  LUX 与 EV 计算  ------------------------------------------ */

/**
 * @brief 通过 lux 计算得到 EV 值，计算公式如下：
 * EV = 2 + log2(lux / 10)
 *
 * @param lux
 * @return
 */
double calc_ev_by_lux(double lux)
{
	double ev = 2 + log2(lux / 10);

	return ev;
}

/**
 * @brief 根据 EV, ISO 和 光圈大小计算快门速度
 * EV0 = 2 + log2(LUX/10) = log2(N*N /T) - log2(ISO/100)
 * 推导出 T = N*N / (2^EV0 + ISO/100)
 *
 * @param ev 以ISO为100时的曝光值
 * @param iso ISO感光度，一般为 100, 200, 400, ...
 * @param N 光圈大小，一般为 1, 1.4, 2, 2.8, ...
 * @return T 快门速度，一般形式为 g_tv_list[] 中的 val；实际返回快门速度档位
 */
uint32_t calc_shutter_speed(double ev, float iso, float N)
{
	double shutter_speed = 0.0;
	uint32_t shutter_speed_idx = g_tv_list_size - 1;
	int i = 0;

	shutter_speed = N * N / (pow(2, ev) + iso / 100);

	for (; i < g_tv_list_size-1; i++) {
		if ((g_tv_list[i].val >= shutter_speed) && (shutter_speed > g_tv_list[i + 1].val)) {
			shutter_speed_idx = i;
			break;
		}
	}

	return shutter_speed_idx;
}

/**
 * @brief  根据 EV, ISO 和快门速度计算光圈大小
 * EV0 = 2 + log2(LUX/10) = log2(N*N / T) - log2(ISO/100)
 * 推导出 N = sqrt((2^ev x ISO x T) / 100)
 *
 * @param ev 以 ISO 为100是的曝光值
 * @param iso ISO 感光度，一般为 100, 200, 400, ...
 * @param sp 快门速度，以秒为单位，例如 32, 16, ..., 1, 1/2, ..., 1/500, 1/1000
 * @return uint32_t 光圈的索引值
 */
uint32_t calc_aperture(double ev, float iso, float sp)
{
	uint32_t aperture_idx = g_av_list_size - 1;;
	float aperture = 1.0;
	int i = 0;

	aperture = sqrt((pow(2, ev) * iso * sp) / 100);

	for (; i < g_av_list_size-1; i++) {
		if (g_av_list[i].val <= aperture && aperture < g_av_list[i+1].val) {
			aperture_idx = i;
			break;
		}
	}

	return aperture_idx;
}
